﻿#include "aes_cbc_pkcs5.h"

#include "openssl/sha.h"
#include "openssl/aes.h"
#include <openssl/hmac.h>
#include <openssl/err.h>
#include <string.h> // for strlen
#include <vector> 
#include <iostream> 
#include <iomanip>

#include "sha1.h"
#include "hmac.h"
#include <memory>

#define AES_BLOCK_SIZE 16
#define MAX_SEND_BUFF_SIZE 10240
#define MAX_PKT_BUFF 10240

std::string hmacsha1_new(const std::string& private_key, const std::string& context) {
    return std::move(mky::hmac<mky::SHA1>(context, private_key));
}
// 将16进制字符串，转成字节数组
static void hex_str_to_byte(const char* source, unsigned char* dest, int sourceLen)
{
    short i;
    unsigned char highByte, lowByte;

    for (i = 0; i < sourceLen; i += 2)
    {
        highByte = toupper(source[i]);
        lowByte  = toupper(source[i + 1]);

        if (highByte > 0x39)
            highByte -= 0x37;
        else
            highByte -= 0x30;

        if (lowByte > 0x39)
            lowByte -= 0x37;
        else
            lowByte -= 0x30;

        dest[i / 2] = (highByte << 4) | lowByte;
    }
}

// 将字节数组，转成16进制字符串
static std::string byte_to_hex_string(const void* buf, size_t len)
{
    std::string ret;
    char tmp[8];
    const uint8_t* data = (const uint8_t*)buf;

    for (size_t i = 0; i < len; i += 16) {
        for (int j = 0; j < 16; ++j) {
            if (i + j < len) {
                int sz = sprintf(tmp, "%.2x", data[i + j]);
                ret.append(tmp, sz);
            }
        }
    }

    return ret;
}
/**********************************************************
函数名：PKCS5Padding
参数：unsigned char *str      --字符串地址
返回值：int                   --正向测试填充后的字符串长度
说明：对初始数据进行PKCS5Padding填充
***********************************************************/
int PKCS5Padding(unsigned char* str, int len)
{
	int remain, i;

	remain = 16 - len % 16;
	//printf("remain = %d\n",remain);
	for (i = 0; i < remain; i++)
	{
		str[len + i] = remain;
		//printf("str[len+i]= %d\n",str[len+i]);
	}
	str[len + i] = '\0';
	return len + remain;
}

/**********************************************************
函数名：DePKCS5Padding
参数：unsigned char *p    --字符串地址
返回值：int               --反填充个数
说明：对明文进行PKCS5Padding填充反填充(去除后面的填充乱码)
***********************************************************/
int DePKCS5Padding(unsigned char* str, int str_len)
{
	int remain, i;

	remain = *(str + str_len - 1);//读取填充的个数
	//printf("remain = %d\n",remain);

	for (i = 0; i < remain; i++) { str--; }
	str++;
	*str = '\0';//截断

	return remain;
}
// PKCS5填充
// 如果明文块少于16个字节（128bit），在明文块末尾补足相应数量的字符，且每个字节的值等于缺少的字符数。
// 比如明文：{1,2,3,4,5,a,b,c,d,e},缺少6个字节，则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6 }
// 如果明文块等于16个字节，则继续填充16个字节16
// 如果明文块大于16个字节，按照16的整数倍计算还需填充多少字节，并进行填充对于的值
static void PKCS5_padding(unsigned char* src, int src_len)
{
    static const int padding_len = 16;
    if (src_len < padding_len) {
        unsigned char pad = padding_len - src_len;
        for (int i = padding_len; i > src_len; --i)
            src[i - 1] = pad;
    }
    else if (src_len == padding_len) {
        for (int i = padding_len; i < padding_len * 2; i++)
            src[i] = padding_len;
    }
    else {
        int pad = ((src_len + padding_len) / padding_len) * padding_len - src_len;
        for (int i = src_len; i < src_len+pad; i++)
            src[i] = pad;
    }
}

// 由于使用PKCS7Padding/PKCS5Padding填充时，最后一个字节肯定为填充数据的长度，
// 所以在解密后可以准确删除填充的数据，而使用ZeroPadding填充时，
// 没办法区分真实数据与填充数据，所以只适合以\0结尾的字符串加解密。
static int PKCS5_unpadding(char* in)
{
    int in_len = strlen(in);
    int pad, pad_len;
    pad = pad_len = in[in_len - 1];
    for (int i = 0; i < pad_len; ++i) {
        if (in[in_len - 1 - i] == pad)
            in[in_len - 1 - i] = 0;
    }
    return pad_len;
}

static int aes_encrypt(const char* in, size_t in_len, const char* key, char* out)
{
    if (!in || !key || !out) return -1;

    PKCS5_padding((unsigned char*)in, in_len);

    AES_KEY enc_key;
    if (AES_set_encrypt_key((const unsigned char*)key, strlen(key)*8, &enc_key) < 0)
        return -1;

    int len = strlen(in);
    int position = 0;

    while (position < len) {
        AES_cbc_encrypt((const unsigned char*)in+position, (unsigned char*)out+position, in_len, &enc_key, (unsigned char*)key,AES_ENCRYPT);
        position += AES_BLOCK_SIZE;
    }

    return position;
}

//static int aes_decrypt(const char* in, size_t in_len, const char* key, char* out)
//{
//    if (!in || !key || !out) return -1;
//
//    //unsigned char iv[AES_BLOCK_SIZE];//加密的初始化向量
//    //for (int i = 0; i < AES_BLOCK_SIZE; ++i)//iv一般设置为全0,可以设置其他，但是加密解密要一样就行
//    //    iv[i] = 0;
//
//    AES_KEY enc_key;
//    if (AES_set_decrypt_key((const unsigned char*)key, 128, &enc_key) < 0)
//        return -1;
//    AES_cbc_encrypt((const unsigned char*)in, (unsigned char*)out, in_len, &enc_key, (unsigned char*)key, AES_DECRYPT);
//    int pad_len = PKCS5_unpadding(out);
//    return in_len - pad_len;
//}
int aes_cbc_decrypt(char* in, int in_len, char* key, char* iv, char* out)
{
	int i;
	AES_KEY aes;

	if (!in || !key || !out) return 0;

	if (AES_set_decrypt_key((unsigned char*)key, strlen(key) * 8, &aes) < 0) {
		printf("fail to set encrypt key");
		return 0;
	}

	AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, in_len, &aes, (unsigned char*)iv, AES_DECRYPT);

	return (in_len - DePKCS5Padding((unsigned char*)out, in_len));
}
int aes_cbc_base64_dec_data(char* in, int in_len, char* iv, char* dst, char* key)
{
	int dec_len = 0;
	unsigned char dec[MAX_PKT_BUFF] = { 0 };
	unsigned char dec_key[17] = { 0 };
	unsigned char enc_iv[17] = { 0 };
	char tmp[MAX_PKT_BUFF] = { 0 };

	memcpy((char*)dec_key, key, 17);
	memcpy((char*)enc_iv, iv, 17);
	//memcpy((char*)base64_decode_out, src,240);

	dec_len = aes_cbc_decrypt(in, in_len, (char*)dec_key, (char*)enc_iv, (char*)dec);

	memcpy(dst, dec, dec_len);
	return dec_len;
}
static std::string aes_decrypt(unsigned char* ciphertext, int ciphertext_len, unsigned char* key,unsigned char* iv) {
    using namespace std;
    EVP_CIPHER_CTX* ctx;
    unsigned char* plaintexts;
    int len;
    int plaintext_len;
    unsigned char* plaintext = new unsigned char[ciphertext_len];
    bzero(plaintext, ciphertext_len);

    /* Create and initialise the context */
    if (!(ctx = EVP_CIPHER_CTX_new())){
        ERR_print_errors_fp(stderr);
        return "";
    }

    /* Initialise the decryption operation. IMPORTANT - ensure you use a key
     * and IV size appropriate for your cipher
     * In this example we are using 256 bit AES (i.e. a 256 bit key). The
     * IV size for *most* modes is the same as the block size. For AES this
     * is 128 bits */
    if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)){
        ERR_print_errors_fp(stderr);
        return "";
    }

    EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);

    /* Provide the message to be decrypted, and obtain the plaintext output.
      * EVP_DecryptUpdate can be called multiple times if necessary
      */
    if (1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)){
        ERR_print_errors_fp(stderr);
        return "";
    }
    plaintext_len = len;

    /* Finalise the decryption. Further plaintext bytes may be written at
     * this stage.
     */
    if (1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) {
        ERR_print_errors_fp(stderr);
        return "";
    }

    plaintext_len += len;

    /* Add the null terminator */
    plaintext[plaintext_len] = 0;

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);
    std::string ret = std::string((char*)plaintext, plaintext_len);
    delete[] plaintext;
    return std::move(ret);
}
std::string aes_encrypt(const std::string& strData, const std::string& strKey)
{
    char key[AES_BLOCK_SIZE + 1] = {0};
    hex_str_to_byte(strKey.data(), (unsigned char*)key, strKey.length());

    char out[256] = {0};
    char in[256] = {0};
    memcpy(in, strData.data(), strData.length()); // 因为会进行pad处理必须单独分配空间

    int len = aes_encrypt(in, strData.length(), key, out);
    if (len == -1)
        return "";

    //size_t len = ((strData.length() + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
    return byte_to_hex_string(out, len);
}
std::string aes_decrypt_old(const std::string& strData, const std::string& strKey, const std::string& strIv)
{
    unsigned char in[MAX_PKT_BUFF] = { 0 };
    hex_str_to_byte(strData.data(), in, strlen(strData.data()));;
    return std::move(aes_decrypt(in,/*strlen(strData)*/strData.length()/2, (unsigned char*)strKey.c_str(), (unsigned char*)strIv.c_str()));
}
std::string aes_decrypt_new(const std::string& strData, const std::string& strKey, const std::string& strIv)
{
	unsigned char in[MAX_PKT_BUFF] = { 0 };
	hex_str_to_byte(strData.data(), in, strlen(strData.data()));;

    // 若strData以00开头会有问题
    char out[MAX_PKT_BUFF] = { 0 };
    int len = aes_cbc_base64_dec_data((char*)in, strData.length()/2, (char*)strIv.c_str(), out, (char*)strKey.c_str());
    if (len < 0)
        return "";
    return std::move(std::string(out, len));
}
